﻿using System;
using System.Collections.Generic;
using UnityEngine;

namespace CWM.Skinn
{
    public static partial class SkinnEx
    {
        public static Mesh Weld(this Mesh source, bool lowPrecision)
        {
            if (!source) return null;

            var sourceData = new MeshVertexData(source);
            var targetData = new MeshVertexData();
            var tempIndices = new Dictionary<int, int>(source.vertices.Length);
            var orignalIndex = new List<int>(source.vertices.Length);
            var welds = 0;
            var subMeshCount = source.subMeshCount;
            var trianglesIndices = new Dictionary<int, int[]>(subMeshCount);
            var precision = lowPrecision ? "0.0" : "0.00";

            for (int i = 0; i < subMeshCount; i++)
            {
                var vertexKeys = new Dictionary<int, int>(source.vertices.Length);
                var triangles = source.GetTriangles(i);
                var newIndices = new List<int>(triangles.Length);
                for (int ii = 0; ii < triangles.Length; ii++)
                {
                    int currentIndex = triangles[ii]; int newIndex;
                    if (!tempIndices.TryGetValue(currentIndex, out newIndex))
                    {
                        var vertexKey = sourceData.GetVertexKey(currentIndex, "", precision);
                        if (vertexKeys.TryGetValue(vertexKey, out newIndex)) { welds++; }
                        else
                        {
                            newIndex = targetData.AddVertexData(sourceData, currentIndex);
                            tempIndices.Add(currentIndex, newIndex);
                            orignalIndex.Add(currentIndex);
                            vertexKeys.Add(vertexKey, newIndex);
                        }
                    }
                    newIndices.Add(newIndex);
                }
                trianglesIndices.Add(i, newIndices.ToArray());
            }

            Mesh newMesh = targetData.GetMesh(trianglesIndices, source.bindposes);
            newMesh.CopyBlendshapes(source, orignalIndex.ToArray());
            newMesh.RecalculateBounds();
            newMesh.name = source.name;
            Debug.LogFormat("({0}) Welds {1}, Old  Vertex Count {2}, New Vertex Count {3}", sourceData.name, welds, source.vertices.Length, newMesh.vertices.Length);

            return newMesh;
        }


        public static byte[] GetSeams(this Mesh mesh)
        {
            const float weld = 1e-8f;
            var vertices = mesh.vertices;
            var uv = mesh.uv;
            var seams = new byte[vertices.Length];
            for (int i = 0; i < vertices.Length; i++)
            {
                for (int ii = 0; ii < vertices.Length; ii++)
                {
                    //if (seams[i] == 1 && seams[ii] == 1) break;
                    var dx = vertices[i].x - vertices[ii].x; var dy = vertices[i].y - vertices[ii].y; var dz = vertices[i].z - vertices[ii].z;
                    var ux = uv[i].x - uv[ii].x; var uy = uv[i].y - uv[ii].y;
                    if (dx * dx + dy * dy + dz * dz < weld && ux * ux + uy * uy > 0) { seams[i] = 1; seams[ii] = 1; break; }
                }
            }
            return seams;
        }

        /// <summary>
        /// returns a parallel array with the index of the first occurrence of each vector.
        /// </summary>
        /// <param name="vectors"></param>
        /// <returns></returns>
        public static int[] GetUniqueIndices(this Vector3[] vectors)
        {
            var indices = new int[vectors.Length];
            var uniqueVectors = new Dictionary<Vector3, int>(vectors.Length);
            for (int i = 0; i < vectors.Length; i++) if (!uniqueVectors.ContainsKey(vectors[i])) uniqueVectors.Add(vectors[i], i);
            for (int i = 0; i < vectors.Length; i++) { int ii; if (uniqueVectors.TryGetValue(vectors[i], out ii)) indices[i] = ii; }
            return indices;
        }
    }
}